package cn.org.wangchangjiu.table2entity.service;

import cn.org.wangchangjiu.table2entity.extension.TypeSettingCache;
import cn.org.wangchangjiu.table2entity.model.ColumnInfo;
import cn.org.wangchangjiu.table2entity.model.TableInfo;
import cn.org.wangchangjiu.table2entity.model.TypeSettingsStorage;
import cn.org.wangchangjiu.table2entity.util.CommonUtil;
import cn.org.wangchangjiu.table2entity.util.MatchType;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
import net.sf.jsqlparser.statement.create.table.CreateTable;
import net.sf.jsqlparser.statement.create.table.NamedConstraint;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.io.StringReader;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Classname CreateTableParser
 * @Description
 * @Date 2023/5/6 17:55
 * @author wangchangjiu
 */
public class CreateTableParser {

    /**
     *  分析创建表语句
     * @param createTableSQLText
     * @return
     */
    public static TableInfo parserJpaEntity(String createTableSQLText) {
        TableInfo tableInfo = new TableInfo();
        CreateTable createTable = parserCreateTable(createTableSQLText);
        String tableName = CommonUtil.dropQuotationMark(createTable.getTable().getName());
        tableInfo.setEntityName(CommonUtil.underlineToHump(tableName, true));
        tableInfo.setAnnotations(Arrays.asList("@Data","@Entity","@Table(name = \""+ tableName +"\")"));
        Set<String> importPackages = CommonUtil.initJpaEntityPackage();
        // 主键
        List<String> primaryKeys = getPrimaryKeys(createTable);

        List<ColumnDefinition> columnDefinitions = createTable.getColumnDefinitions();
        if(columnDefinitions != null && columnDefinitions.size() > 0){
            // 列信息
            List<ColumnInfo> columnInfos = getColumnInfos(importPackages, primaryKeys, columnDefinitions, true);
            tableInfo.setColumns(columnInfos);
        }

        tableInfo.setImportPackage(importPackages);

        return tableInfo;
    }


    public static TableInfo parserMyBatisPlusEntity(String createTableSQLText) {
        TableInfo tableInfo = new TableInfo();
        CreateTable createTable = parserCreateTable(createTableSQLText);

        String tableName = CommonUtil.dropQuotationMark(createTable.getTable().getName());
        tableInfo.setEntityName(CommonUtil.underlineToHump(tableName, true));

        tableInfo.setAnnotations(Arrays.asList("@Data","@TableName(value =\""+ tableName +"\")"));
        Set<String> importPackages = CommonUtil.initMybatisPlusEntityPackage();

        // 主键
        List<String> primaryKeys = getPrimaryKeys(createTable);

        List<ColumnDefinition> columnDefinitions = createTable.getColumnDefinitions();
        if(columnDefinitions != null && columnDefinitions.size() > 0){
            // 列信息
            List<ColumnInfo> columnInfos = getColumnInfos(importPackages, primaryKeys, columnDefinitions, false);
            tableInfo.setColumns(columnInfos);
        }
        tableInfo.setImportPackage(importPackages);
        return tableInfo;
    }

    @NotNull
    private static CreateTable parserCreateTable(String createTableSQLText) {
        CreateTable createTable;
        try {
            Statement sqlStmt = CCJSqlParserUtil.parse(new StringReader(createTableSQLText));

            if (!(sqlStmt instanceof CreateTable)) {
                throw new RuntimeException("Illegal table creation SQL statement");
            }

            createTable = CreateTable.class.cast(sqlStmt);

            if (createTable.getSelect() != null || createTable.getLikeTable() != null) {
                throw new RuntimeException("create table syntax not support select or likeTable");
            }
        } catch (Exception e){
            throw new RuntimeException(e);
        }
        return createTable;
    }

    @NotNull
    private static List<String> getPrimaryKeys(CreateTable createTable) {
        List<String> primaryKeys = new ArrayList<>();
        createTable.getIndexes().stream().filter(item -> {
            if(item instanceof NamedConstraint){
                NamedConstraint namedConstraint = NamedConstraint.class.cast(item);
                return "PRIMARY KEY".equalsIgnoreCase(namedConstraint.getType());
            }
            return false;
        }).forEach(item -> {
            NamedConstraint namedConstraint = NamedConstraint.class.cast(item);
            if(namedConstraint.getColumnsNames() != null && namedConstraint.getColumnsNames().size() > 0){
                primaryKeys.addAll(namedConstraint.getColumnsNames().stream().map(name -> CommonUtil.dropQuotationMark(name)).collect(Collectors.toList()));
            }
        });
        return primaryKeys;
    }

    /**
     *  分析列信息
     * @param importPackages
     * @param primaryKeys
     * @param columnDefinitions
     * @return
     */
    private static List<ColumnInfo> getColumnInfos(Set<String> importPackages, List<String> primaryKeys, List<ColumnDefinition> columnDefinitions, boolean isJpaEntity) {
        List<ColumnInfo> columnInfos = new ArrayList<>();
        columnDefinitions.stream().forEach(columnDefinition -> {
            ColumnInfo columnInfo = new ColumnInfo();

            // 列名
            String columnName = CommonUtil.dropQuotationMark(columnDefinition.getColumnName());
            columnInfo.setJdbcColumnName(columnName);
            columnInfo.setName(CommonUtil.underlineToHump(columnName, false));

            // 列注解
            List<String> annotations = new ArrayList<>();
            if(isJpaEntity){
                if(primaryKeys.contains(columnName)){
                    // 主键
                    annotations.add("@Id");
                    // AUTO_INCREMENT
                    Optional<String> optionalAutoIncrement = columnDefinition.getColumnSpecs().stream().filter(item -> "AUTO_INCREMENT".equalsIgnoreCase(item)).findFirst();
                    if(optionalAutoIncrement.isPresent()){
                        annotations.add("@GeneratedValue(strategy = GenerationType.IDENTITY)");
                    }
                    columnInfo.setPrimaryKey(true);
                }
                annotations.add("@Column(name = \"" + columnName + "\")");
            } else {
                if(primaryKeys.contains(columnName)){
                    // 主键
                    // @TableId(value = "id", type = IdType.AUTO)
                    Optional<String> optionalAutoIncrement = columnDefinition.getColumnSpecs().stream().filter(item -> "AUTO_INCREMENT".equalsIgnoreCase(item)).findFirst();
                    if(optionalAutoIncrement.isPresent()){
                        annotations.add("@TableId(value = \"" + columnName + "\", type = IdType.AUTO)");
                    } else {
                        annotations.add("@TableId(value = \"" + columnName + "\")");
                    }
                    columnInfo.setPrimaryKey(true);
                }
                annotations.add("@TableField(value = \"" + columnName + "\")");
            }

            columnInfo.setAnnotations(annotations);

            // 列类型
            String dataType = columnDefinition.getColDataType().getDataType();

            TypeSettingsStorage.TypeSetting typeSetting = getJavaOrJdbcType(dataType);

            columnInfo.setJdbcType(StringUtils.isEmpty(typeSetting.getJdbcType()) ? CommonUtil.mybatisJdbcType(dataType) : typeSetting.getJdbcType());

            columnInfo.setType(typeSetting.getJavaType());
            columnInfo.setTypeImportPck("import " + typeSetting.getImportPck() + ";");
            importPackages.add("import " + typeSetting.getImportPck() + ";");

            // 列描述
            int commentIndex = columnDefinition.getColumnSpecs().indexOf("COMMENT");
            if(commentIndex < 0){
                commentIndex = columnDefinition.getColumnSpecs().indexOf("comment");
            }
            if(commentIndex > 0){
                List<String> commentDesc = columnDefinition.getColumnSpecs().subList(commentIndex + 1, commentIndex + 2);
                columnInfo.setDesc(commentDesc.get(0));
            }

            columnInfos.add(columnInfo);

        });
        return columnInfos;
    }

    private static TypeSettingsStorage.TypeSetting getJavaOrJdbcType(String dbType) {
        List<TypeSettingsStorage.TypeSetting> settings = TypeSettingCache.getInstance().getSettings();
        for (TypeSettingsStorage.TypeSetting setting: settings) {
            if (MatchType.ORDINARY.name().equalsIgnoreCase(setting.getMatchType())) {
                if (dbType.equalsIgnoreCase(setting.getColumnType())) {
                    return setting;
                }
            } else {
                // 不区分大小写的正则匹配模式
                if (Pattern.compile(setting.getColumnType(), Pattern.CASE_INSENSITIVE).matcher(dbType).matches()) {
                    return setting;
                }
            }
        }
        return TypeSettingsStorage.TypeSetting.objectType();
    }
}
